#include <UnitTest++.h>
#include "..\trigger_state.h"
#include "trigger_state_fixtures.h"
#include <stdarg.h>
#include <algorithm>
#include <iterator>
#include <cmath>

TEST(PassAlways)
{
    CHECK(true);
}

TEST(CreatePreTrigFillingState)
{
    double fakeData[] = {1,2,3,4,5};
    arrayHolder dh(GROUPBYSCAN, 5,1,fakeData);
    ChanPointer cp(dh,0);
    Triggered tsDone;
    Armed tsArmed(&tsDone, RISING, 2.5, cp);
    PreTrigFull tsFull(&tsArmed, RISING, 2.5, cp);
    PreTrigFilling tsFilling(&tsFull, 5);
    TriggerState* pTS = &tsFilling;

    CHECK_EQUAL(-1, pTS->getTempIndex());
    CHECK_EQUAL(-1, pTS->getTempIndex());
}

TEST(UpdateReturnsTriggerStateInstancePointer)
{
    double fakeData[] = {1,2,3,4,5};
    arrayHolder dh(GROUPBYSCAN, 5,1,fakeData);
    ChanPointer cp(dh,0);
    Triggered tsDone;
    Armed tsArmed(&tsDone, RISING, 2.5, cp);
    PreTrigFull tsFull(&tsArmed, RISING, 2.5, cp);
    PreTrigFilling tsFilling(&tsFull, 5);
    TriggerState* pTS = &tsFilling;
    CHECK_EQUAL(&tsFilling, pTS->update(0));  //can compare pointers
    CHECK_EQUAL(tsFilling, *pTS->update(0));  // or can compare objects
    CHECK(tsFilling == *pTS->update(0));      // equality operator also works
}

TEST(TestingSamplesAvailable)
{
    double fakeData[] = {1,2,3,4,5};
    arrayHolder dh(GROUPBYSCAN, 5,1,fakeData);
    ChanPointer cp(dh,0);

    Triggered tsDone;
    Armed tsArmed(&tsDone, RISING, 2.5, cp);
    PreTrigFull tsFull(&tsArmed, RISING, 2.5, cp);
    PreTrigFilling tsFilling(&tsFull, 5);
    TriggerState* pTS = &tsFilling;

    CHECK_EQUAL(0, tsFilling.getAvailableSampPerChan());
    CHECK_EQUAL(-1, pTS->getTempIndex());
    CHECK_EQUAL(&tsFilling, pTS->update(0));
    CHECK_EQUAL(-1, pTS->update(0)->getTempIndex());

    CHECK_EQUAL(0, tsFilling.getAvailableSampPerChan());
    pTS->update(3);
    CHECK_EQUAL(3, tsFilling.getAvailableSampPerChan());
    pTS->update(2);
    CHECK_EQUAL(5, tsFilling.getAvailableSampPerChan());
}



TEST_FIXTURE(PreTrigFillingFixture, CheckArmingLevelandSlope)
{
    CHECK_EQUAL(2.5, tsFull.getArmingLevel());
    CHECK_EQUAL(RISING == RISING ? FALLING : RISING, tsFull.getArmingSlope()); // armslope opposite
    CHECK_EQUAL(&tsFull, tsFull.update(0));
}

TEST(ArmingLogicClass)
{
    double fakeData[] = {1,2,3,4,5};
    arrayHolder dh(GROUPBYSCAN, 5,1,fakeData);
    ChanPointer cp(dh,0);
    VoltageLevelLogic aOne(RISING == RISING ? FALLING : RISING, 2.5, cp); // arming slope opposite of trigger slope
    CHECK_EQUAL(0, aOne(2, 0));
    CHECK_EQUAL(0, aOne(5, 0));
    CHECK_EQUAL(-1, aOne(5, 2));
    VoltageLevelLogic aTwo(FALLING == RISING ? FALLING : RISING, 2.5, cp);
    CHECK_EQUAL(2, aTwo(5, 0));
    CHECK_EQUAL(3, aTwo(5, 3));
}

TEST_FIXTURE(PreTrigFillingFixture, ArmingHappensAndChangesClass)
{
    double fakeData[] = {5,4,3,2,1,1,3,5};
    arrayHolder dh(GROUPBYSCAN, 8,1,fakeData);
    ChanPointer cp(dh,0);

    Triggered tsDone;
    Armed tsArmed(&tsDone, RISING, 2.5, cp);
    PreTrigFull tsFull(&tsArmed, RISING, 2.5, cp);
    PreTrigFilling tsFilling(&tsFull, 5);
    TriggerState* pTS = &tsFilling;

    pTS  = &tsFull;
    CHECK_EQUAL(tsFull, *pTS->update(2));
    CHECK_EQUAL(tsArmed, *pTS->update(4));
}

TEST(ArmedCanChangeToTriggered)
{
    Triggered done;
    CHECK_EQUAL(-1, done.getTempIndex());
    //done.update(5,0);  // should cause immediate exit
    double fakeData[] = {0,1,2,3,4,5};
    arrayHolder dh(GROUPBYSCAN, 6, 1,fakeData);
    ChanPointer cp(dh,0);

    Armed a(&done, RISING, 2.5, cp);
    CHECK_EQUAL(&a, a.update(2));
    CHECK_EQUAL(&done, a.update(6));
    CHECK_EQUAL(3, a.getTempIndex());
}

TEST(ArmedStateUsesIndex)
{
    Triggered done;
    double fakeData[] = {5,4,3,2,1,0};
    arrayHolder dh(GROUPBYSCAN, 5,1,fakeData);
    ChanPointer cp(dh,0);

    Armed a(&done, RISING, 2.5, cp);
    CHECK_EQUAL(&a, a.updateInstance(2, 2));
    CHECK_EQUAL(&a, a.updateInstance(6, 3));
    CHECK_EQUAL(&done, a.updateInstance(6, 2));
    CHECK_EQUAL(&done, a.update(6));
    CHECK_EQUAL(0, a.getTempIndex());
}

TEST(UnitTestMemoryOutstream)
{
    // Change to Unittest config.h to make MemoryOutStream
    // derive from std::ostringstream default is custom class!
    std::ostringstream* oss;
    UnitTest::MemoryOutStream mos;
    oss = &mos;
}

TEST(ClassEqualityandOStreamOperators)
{
    double fakeData[] = {5,4,3,2,1,0};
    arrayHolder dh(GROUPBYSCAN, 6, 1,fakeData);
    ChanPointer cp(dh,0);

    Triggered t1;
    Armed a1(&t1, RISING, 2.5, cp);
    PreTrigFull full(&t1, RISING, 2.5, cp);
    PreTrigFilling fill(&full, 0);

    CHECK(t1 == t1);
//    std::cout << "hello " << t1 << std::endl;  //should work, uses ostream
    CHECK_EQUAL(t1, t1);
//    CHECK_EQUAL(t1, full);  //should fail
//    CHECK_EQUAL(t1, fill);  // should fail
    std::ostringstream oss;
    oss << t1 << a1 << full << fill;
    CHECK_EQUAL("TriggeredArmedPreTrigFullPreTrigFilling", oss.str());
}

TEST(SineDataGeneration)
{
    CHECK_CLOSE(3.14, 4*atan(1), 1e-2);
    double y[40];
    for(int x=0; x < 40; x++)
        y[x] = -5.0 + x/4.0 + 3.0 * sin(x/2.0);
    CHECK_CLOSE(-5.3767728239894153, y[10], 1e-8);
}

TEST_FIXTURE(SineWaveFixture, TestSineWaveFixture)
{
    CHECK_CLOSE(-5.3767728239894153, y[10], 1e-8);
}

